package com.pet.system.security.jwt;

import com.auth0.jwt.JWT;
import com.auth0.jwt.JWTVerifier;
import com.auth0.jwt.algorithms.Algorithm;
import com.auth0.jwt.exceptions.JWTDecodeException;
import com.auth0.jwt.interfaces.DecodedJWT;
import com.pet.system.domain.model.LoginUser;
import lombok.AllArgsConstructor;
import org.apache.commons.lang3.StringUtils;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.context.annotation.Configuration;
import org.springframework.stereotype.Component;

import javax.servlet.http.HttpServletRequest;
import java.util.Date;
import java.util.HashMap;
import java.util.Map;

/**
 * jwt工具 jwt刷新机制： https://www.sundayfine.com/jwt-refresh-token/ - 考虑页面多个请求，被替换的 老token 还要预留有效时间
 *
 * <p>-- 还一种：前端处理token刷新 【即将失效:,在用,】
 *
 * @author Centaurea
 */
@Configuration
@EnableConfigurationProperties({JwtProperties.class})
@Component
@AllArgsConstructor
public class JwtTools {
  private final JwtProperties jwtConfig;

  /**
   * 生成签名
   *
   * @param user
   * @return
   */
  public String sign(LoginUser user) {
    Date expiresAt = new Date((new Date()).getTime() + jwtConfig.getExpire() * 1000);
    Map<String, Object> header = new HashMap<>(2);
    header.put("typ", "JWT");
    header.put("alg", "HS256");
    return JWT.create()
        .withHeader(header)
        .withClaim("userId", user.getId())
        .withClaim("username", user.getUsername())
        .withExpiresAt(expiresAt)
        .sign(
            Algorithm.HMAC256(
                StringUtils.isEmpty(jwtConfig.getSecret())
                    ? user.getPassword()
                    : jwtConfig.getSecret()));
  }

  public boolean refreshToken(String jwtToken) {
    // 刷新并延长 token 有效期
    return true;
  }

  /**
   * 校验token是否过期
   *
   * @return
   */
  public boolean isExpired(String jwtToken) {
    DecodedJWT jwt = JWT.decode(jwtToken);
    return jwt.getExpiresAt().before(new Date());
  }

  public boolean verify(String token, LoginUser user) {
    try {
      Algorithm algorithm =
          Algorithm.HMAC256(
              StringUtils.isEmpty(jwtConfig.getSecret())
                  ? user.getPassword()
                  : jwtConfig.getSecret());
      JWTVerifier verifier =
          JWT.require(algorithm)
              .withClaim("userId", user.getId())
              .withClaim("username", user.getUsername())
              .build();
      DecodedJWT jwt = verifier.verify(token);
      return jwt != null;
    } catch (Exception exception) {
      return false;
    }
  }

  public String getJwtToken(HttpServletRequest request) {
    String token = request.getHeader(jwtConfig.getHeaderName());
    if (StringUtils.isEmpty(token)) {
      token = request.getParameter(jwtConfig.getHeaderName());
    }
    return token;
  }

  public LoginUser getLoginUser(HttpServletRequest request) {
    if (this.isAccessAllowed(request)) {
      String token = this.getJwtToken(request);
      return this.getLoginUser(token);
    }
    return null;
  }

  /**
   * 获取登录用户， 密码置空
   *
   * @param token
   * @return
   */
  public LoginUser getLoginUser(String token) {
    DecodedJWT jwt = JWT.decode(token);
    Integer userId = jwt.getClaim("userId").asInt();
    String username = jwt.getClaim("username").asString();
    return new LoginUser(userId, username, null);
  }

  /**
   * 判断是否允许访问
   *
   * @param request
   * @return
   */
  public boolean isAccessAllowed(HttpServletRequest request) {
    String token = this.getJwtToken(request);
    if (StringUtils.isEmpty(token)) {
      throw new SecurityException("无合法token值");
    }
    try {
      if (isExpired(token)) {
        throw new SecurityException("token已过期");
      }
    } catch (JWTDecodeException e) {
      throw new SecurityException("该token无效");
    }
    return true;
  }
}
